#include <PubSubClient.h>
/* 依赖 PubSubClient 2.4.0 */
#include <ArduinoJson.h>
/* 依赖 ArduinoJson 5.13.4 */
#include "esp_http_client.h"
#include "esp_camera.h"

#include <ETH.h>
#include <WiFi.h>
#include <WiFiAP.h>
#include <WiFiClient.h>
#include <WiFiGeneric.h>
#include <WiFiMulti.h>
#include <WiFiScan.h>
#include <WiFiServer.h>
#include <WiFiSTA.h>
#include <WiFiType.h>
#include <WiFiUdp.h>
#include<esp_wifi.h>

#define LED_BUILTIN    2

/* 连接您的WIFI SSID和密码 */
#define WIFI_SSID        "IOT123"//替换自己的WIFI
#define WIFI_PASSWD      "20010720"//替换自己的WIFI

/* 设备证书信息*/
#define PRODUCT_KEY       "a1XbdXpWhHG"
#define DEVICE_NAME       "WATER"
#define DEVICE_SECRET     "27d291efa2bb4b0594b63a1a7a6c1883"
#define REGION_ID         "cn-shanghai"

/* 线上环境域名和端口号，不需要改 */
#define MQTT_SERVER       PRODUCT_KEY ".iot-as-mqtt." REGION_ID ".aliyuncs.com"
#define MQTT_PORT         1883
#define MQTT_USRNAME      DEVICE_NAME "&" PRODUCT_KEY

#define CLIENT_ID         "WATER|securemode=3,signmethod=hmacsha1,timestamp=999|"
// 请使用以上说明中的加密工具或参见MQTT-TCP连接通信文档加密生成password。
// 加密明文是参数和对应的值（clientIdesp8266deviceName${deviceName}productKey${productKey}timestamp1234567890）按字典顺序拼接
// 密钥是设备的DeviceSecret
#define MQTT_PASSWD       "76AFC3109AD7131741D6D36E6E2E1607B5601641"

///////////////////////////////////////////////////////////////////////////
/*********************需要修改的地方**********************/
int capture_interval = 20 * 1000;      // 默认20秒上传一次，可更改（本项目是自动上传，如需条件触发上传，在需要上传的时候，调用take_send_photo()即可）
const char*  uid = "a66566face7a8b360d26fc4bbc5c3bc1";    //用户私钥，巴法云控制台获取
const char*  topic = "IOT";     //主题名字，可在控制台新建
const char*  wechatMsg = "";          //如果不为空，会推送到微信，可随意修改，修改为自己需要发送的消息
const char*  wecomMsg = "";          //如果不为空，会推送到企业微信，推送到企业微信的消息，可随意修改，修改为自己需要发送的消息
const char*  urlPath = "";           //如果不为空，会生成自定义图片链接，自定义图片上传后返回的图片url，url前一部分为巴法云域名，第二部分：私钥+主题名的md5值，第三部分为设置的图片链接值。
/********************************************************/

const char*  post_url = "http://images.bemfa.com/upload/v1/upimages.php"; // 默认上传地址
static String httpResponseString;//接收服务器返回信息
bool internet_connected = false;
long current_millis;
long last_capture_millis = 0;

// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

/////////////////////////////////////////////////////////////////////////////////

#define ALINK_BODY_FORMAT         "{\"id\":\"water\",\"version\":\"1.0.0\",\"method\":\"thing.event.property.post\",\"params\":{\"SoilMoisture\":%d}}\r"
#define ALINK_TOPIC_PROP_POST     "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"

unsigned long lastMs = 0;
int LEDsw  = 1;
WiFiClient espClient;
PubSubClient  client(espClient);
const int arduinoPin = 4;
int SoilMoisture=50;

void callback(char *topic, byte *payload, unsigned int length)
{
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  payload[length] = '\0';
  Serial.println((char *)payload);

  StaticJsonBuffer<400> jsonBuffer1;
  JsonObject& root1 = jsonBuffer1.parseObject((char *)payload);
  if (!root1.success()) {
    Serial.println("parseObject()1 failed");
    Serial.println("END");
    return;
  }
  const char* mmethod = root1["method"];
  Serial.println(mmethod);
  long mid = root1["id"];
  Serial.println(mid);
  String message = root1["params"];
  Serial.println(message);

  StaticJsonBuffer<400> jsonBuffer2;
  JsonObject& root2 = jsonBuffer2.parseObject(message);
  if (!root2.success()) {
    Serial.println("parseObject()2 failed");
    Serial.println("END");
    return;
  }
  int mLightSwitch = root2["SoilMoisture"];
  Serial.println(mLightSwitch);
  //LEDsw = mLightSwitch;
}

void wifiInit()
{
  wifi_init_config_t config= WIFI_INIT_CONFIG_DEFAULT();
  esp_wifi_init(&config);
    delay(1000);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWD);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    Serial.println("WiFi not Connect");
  }

  Serial.println("Connected to AP");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("espClient [");
  client.setServer(MQTT_SERVER, MQTT_PORT);   /* 连接WiFi之后，连接MQTT服务器 */
  client.setCallback(callback);
}

void wifiDisconnect()
{
  esp_wifi_stop();
  delay(1000);
  esp_wifi_deinit();
  //while (WiFi.status() != WL_DISCONNECTED)
  //{
   // delay(1000);
   // Serial.println("WiFi still Connect");
  //}
 // Serial.println("WiFi alrady disconneted");
}

void mqttCheckConnect()
{
  while (!client.connected())
  {
    Serial.println("Connecting to MQTT Server ...");
    if (client.connect(CLIENT_ID, MQTT_USRNAME, MQTT_PASSWD))
    {
      Serial.println("MQTT Connected!");
    }
    else
    {
      Serial.print("MQTT Connect err:");
      Serial.println(client.state());
      delay(5000);
    }
  }
}

void mqttIntervalPost()
{
  char param[32];
  char jsonBuf[128];
  sprintf(jsonBuf, ALINK_BODY_FORMAT, SoilMoisture);
  Serial.println("上传数据：");
  Serial.print("SoilMoisture: ");
  Serial.println(SoilMoisture);
  Serial.println(jsonBuf);
  boolean d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
  Serial.print("publish:0=失败;1=成功    publish:");
  Serial.println(d);
}

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(arduinoPin, INPUT);

  
  //  pinMode(SENSOR_PIN,  OUTPUT);
  /* initialize serial for debugging */
  Serial.begin(115200);
  Serial.println("Start");
  wifiInit();

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
}

/********http请求处理函数*********/
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
  if (evt->event_id == HTTP_EVENT_ON_DATA)
  {
    httpResponseString.concat((char *)evt->data);
  }
  return ESP_OK;
}


/********推送图片*********/
static esp_err_t take_send_photo()
{
  Serial.println("Taking picture...");
  camera_fb_t * fb = NULL;
  esp_err_t res = ESP_OK;

  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return ESP_FAIL;
  }

  httpResponseString = "";
  esp_http_client_handle_t http_client;
  esp_http_client_config_t config_client = {0};
  config_client.url = post_url;
  config_client.event_handler = _http_event_handler;
  config_client.method = HTTP_METHOD_POST;
  http_client = esp_http_client_init(&config_client);
  esp_http_client_set_post_field(http_client, (const char *)fb->buf, fb->len);//设置http发送的内容和长度
  esp_http_client_set_header(http_client, "Content-Type", "image/jpg"); //设置http头部字段
  esp_http_client_set_header(http_client, "Authorization", uid);        //设置http头部字段
  esp_http_client_set_header(http_client, "Authtopic", topic);          //设置http头部字段
  esp_http_client_set_header(http_client, "wechatmsg", wechatMsg);      //设置http头部字段
  esp_http_client_set_header(http_client, "wecommsg", wecomMsg);        //设置http头部字段
  esp_http_client_set_header(http_client, "picpath", urlPath);          //设置http头部字段
  esp_err_t err = esp_http_client_perform(http_client);//发送http请求
  if (err == ESP_OK) {
    //json数据解析
    StaticJsonBuffer<200> doc;
    JsonObject& obj = doc.parseObject(httpResponseString);
    String url = obj["url"];
   // Serial.println(url);//打印获取的URL
  }
  esp_http_client_cleanup(http_client);
  esp_camera_fb_return(fb);
}



// the loop function runs over and over again forever
void loop()
{
  if (millis() - lastMs >= 5000)
  {
    lastMs = millis();
     mqttCheckConnect();
     take_send_photo();  //传图片
     //delay(5000);
     //wifiDisconnect();
     //delay(5000);
     SoilMoisture=analogRead(arduinoPin);
    // Serial.printf("input:%d\n",SoilMoisture);
     //wifiInit();
   //  mqttCheckConnect();
    mqttIntervalPost();  //上报土壤湿度信息
  /*  if (client.available()) //如果有数据可读取
     {
         String line = client.readStringUntil('\n'); //读取数据到换行符
         Serial.print("读取到数据：");
         Serial.println(line);
            //  client.write(line.c_str()); //将收到的数据回发
     }*/
  }
  
  client.loop();
  /*if (LEDsw == 0) {
    //Serial.println("led_off");
    //Serial.println(LEDsw);  
    digitalWrite(LED_BUILTIN, LOW);
  } else  if (LEDsw == 1) {
    //Serial.println(LEDsw);
    //Serial.println("led_on");
    digitalWrite(LED_BUILTIN, HIGH);
  }*/
  delay(5000);
}